/*
 * Copyright 2014 Semidata. All rights reserved.
 */
package com.semidata.rtd.engine.dynamic;

import java.net.URL;
import java.net.URLClassLoader;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author shewei@semidata.com, She Wei
 * 
 */
public class DynamicClassLoader extends URLClassLoader {

	private Set<Pattern> whiteList = new HashSet<Pattern>();

	private Set<Pattern> blackList = new HashSet<Pattern>();

	private ThreadLocal<ClassObjectInMemory> compiledClassObject = new ThreadLocal<>();
	
	public DynamicClassLoader() {
		this(new URL[]{}, new String[] {"*"}, new String[]{});
	}

	public DynamicClassLoader(URL[] urls, String[] whiteList, String[] blackList) {
		super(urls);

		for (String i : whiteList) {
			String j = "^" + i.replace(".", "\\.").replace("*", ".+") + "$";
			Pattern p = Pattern.compile(j);
			this.whiteList.add(p);
		}

		for (String i : blackList) {
			String j = "^" + i.replace(".", "\\.").replace("*", ".+") + "$";
			Pattern p = Pattern.compile(j);
			this.blackList.add(p);
		}
	}

	public Class<?> loadClass(String fullName, ClassObjectInMemory obj)
	        throws ClassNotFoundException {
		compiledClassObject.set(obj);
		return loadClass(fullName);
	}

	public Class<?> loadClass(String name) throws ClassNotFoundException {
		// System.err.println("loading " + name);

		boolean match = false;
		for (Pattern p : this.whiteList) {
			Matcher m = p.matcher(name);
			if (m.find()) {
				match = true;
				break;
			}
		}

		if (match == false) {
			throw new ClassNotFoundException("referenced class : " + name
			        + " forbidden");
		}

		for (Pattern p : this.blackList) {
			Matcher m = p.matcher(name);
			if (m.find()) {
				throw new ClassNotFoundException("referenced class : " + name
				        + " forbidden");
			}
		}

		return super.loadClass(name);
	}

	protected Class<?> findClass(String name) throws ClassNotFoundException {

		ClassObjectInMemory obj = compiledClassObject.get();
		compiledClassObject.remove();
		
		if (obj != null) {
			byte[] array = obj.toByteArray();
			//String fullName = name + obj.getKind().extension;
			return super.defineClass(name, array, 0, array.length);
		}
		return super.findClass(name);
	}

}
